package spring.springbucks;

import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.client.RestTemplate;
import spring.springbucks.enums.OrderStateEnum;
import spring.springbucks.integration.Barista;
import spring.springbucks.service.CoffeeItemService;
import spring.springbucks.service.impl.CoffeeOrderManagementServiceImpl;
import spring.springbucks.strategy.CustomConnectionKeepAliveStrategy;
import spring.springentity.entity.table.CoffeeEntity;
import spring.springentity.entity.table.OrderEntity;
import spring.springentity.repository.CoffeeRepository;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

@Slf4j
@EnableTransactionManagement(mode = AdviceMode.PROXY)
// 第三次作业时注释 @EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
@SpringBootApplication
@EnableJpaRepositories
@ComponentScan({"spring.*"})
@MapperScan("spring.springentity.mapper")
@EnableDiscoveryClient
 @EnableBinding(Barista.class)
public class SpringBucksApplication  {  //implements WebMvcConfigurer

  @Autowired
  private CoffeeRepository coffeeRepository;

  @Autowired
  private CoffeeItemService coffeeService;

  @Autowired
  private CoffeeOrderManagementServiceImpl orderService;

  //  @Autowired
  //  private DataSource dataSource;

  @Resource
  private DruidDataSource druidDataSource;

  public static void main(String[] args) {
    SpringApplication.run(SpringBucksApplication.class, args);
  }

  //@Override
  //public void addInterceptors(InterceptorRegistry registry) {
  //  registry.addInterceptor(new ApiInterceptor())
  //    .addPathPatterns("/coffee/**").addPathPatterns("/order/**");
  //}





  //@Bean
  //public RedisTemplate<String, CoffeeEntity> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
  //  RedisTemplate<String, CoffeeEntity> template = new RedisTemplate<>();
  //  template.setConnectionFactory(redisConnectionFactory);
  //  return template;
  //}
  //@Override
  //public void run(ApplicationArguments args) throws Exception {
  //  Connection conn = druidDataSource.getConnection();
  //  log.info("conn: {}", conn.getClientInfo());
  //  // 使用 Druid + mybatis + redis 改造 springbucks 项目
  //  homework1();
  //  // 使用 Druid 实现无侵入式 SQL 校验功能
  //  homework2();
  //
  //}

  /**
   * 使用 Druid + mybatis + redis 改造 springbucks 项目
   * PS:此处实际使用的是mybatis plus+在线redis
   */
  private void homework1() {
    // 分页
    Page page = PageHelper.startPage(1, 10);
    log.info("All Coffee: {}", page.doSelectPageInfo(() -> coffeeRepository.list()));

    // 单条业务查询
    Optional<CoffeeEntity> latte = coffeeService.findOneCoffee("Latte");
    if (latte.isPresent()) {
      OrderEntity order = orderService.createOrder("Li Lei", latte.get().getName());
      log.info("Update INIT to PAID: {}", orderService.updateState(order, OrderStateEnum.PAID));
      log.info("Update PAID to INIT: {}", orderService.updateState(order, OrderStateEnum.INIT));
    }

    // 单数据插入&查询
    CoffeeEntity cafeItem = new CoffeeEntity();
    cafeItem.setName("TEST_COFFEE");
    cafeItem.setPrice(BigDecimal.valueOf(12345));
    coffeeRepository.save(cafeItem);
    CoffeeEntity cafeItem2 = coffeeRepository.getOne(new QueryWrapper<CoffeeEntity>().eq("NAME", "TEST_COFFEE"));
    log.info("TEST_COFFEE Coffee: {}", cafeItem2);
    // 删除（逻辑删除，如果需要物理删除，则改继承entity类）
    coffeeRepository.remove(new QueryWrapper<CoffeeEntity>().eq("NAME", "TEST_COFFEE"));
    cafeItem2 = coffeeRepository.getOne(new QueryWrapper<CoffeeEntity>().eq("NAME", "TEST_COFFEE"));
    log.info("TEST_COFFEE Coffee after remove: {}", cafeItem2);
  }

  /**
   * 使用 Druid 实现无侵入式 SQL 校验功能
   */
  private void homework2() {
    // * 不允许进行 SQL 拼接。
    // 使用 spring.datasource.druid.filters=wall 实现
    try {
      coffeeRepository.list(new QueryWrapper<CoffeeEntity>().last("where exists(select 1 from T_COFFEE)"));
    } catch (RuntimeException e) {
      log.info("不允许进行 SQL 拼接");
      e.printStackTrace();
    }

    // * 条件参数中，如果有 in ，in 列表不能超过 10 个。
    List<String> paramList = new ArrayList<>();

    paramList.add("TEST_COFFEE1");
    paramList.add("TEST_COFFEE2");
    paramList.add("TEST_COFFEE3");
    paramList.add("TEST_COFFEE4");
    paramList.add("TEST_COFFEE5");
    paramList.add("TEST_COFFEE6");
    paramList.add("TEST_COFFEE7");
    paramList.add("TEST_COFFEE8");
    paramList.add("TEST_COFFEE9");
    paramList.add("TEST_COFFEE10");
    paramList.add("TEST_COFFEE11");
    paramList.add("TEST_COFFEE12");
    try {
      coffeeRepository.list(new QueryWrapper<CoffeeEntity>().in("NAME", paramList).lt("PRICE", 10));
    } catch (RuntimeException e) {
      e.printStackTrace();
    }
  }




  @Bean
  public HttpComponentsClientHttpRequestFactory requestFactory() {
    PoolingHttpClientConnectionManager connectionManager =
      new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS);
    connectionManager.setMaxTotal(200);
    connectionManager.setDefaultMaxPerRoute(20);

    CloseableHttpClient httpClient = HttpClients.custom()
      .setConnectionManager(connectionManager)
      .evictIdleConnections(30, TimeUnit.SECONDS)
      .disableAutomaticRetries()
      // 有 Keep-Alive 认里面的值，没有的话永久有效
      //.setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)
      // 换成自定义的
      .setKeepAliveStrategy(new CustomConnectionKeepAliveStrategy())
      .build();

    HttpComponentsClientHttpRequestFactory requestFactory =
      new HttpComponentsClientHttpRequestFactory(httpClient);

    return requestFactory;
  }

  @LoadBalanced
  @Bean
  public RestTemplate restTemplate(RestTemplateBuilder builder) {
    return builder
      .setConnectTimeout(Duration.ofMillis(100))
      .setReadTimeout(Duration.ofMillis(500))
      .requestFactory(this::requestFactory)
      .build();
  }

}

